Skip to content

fix(client): location buffer — off-thread, flicker-free, reliable slider commit#351

Open
mbarrenechea wants to merge 7 commits into
developfrom
fix/location-geometry-buffer-memo
Open

fix(client): location buffer — off-thread, flicker-free, reliable slider commit#351
mbarrenechea wants to merge 7 commits into
developfrom
fix/location-geometry-buffer-memo

Conversation

@mbarrenechea

Copy link
Copy Markdown
Member

Summary

Fixes the location buffer behaving badly when adjusting the buffer slider — disappearing, flickering, freezing the UI on long polylines, and (most reported) not resizing on fast drags.

  • Off the main thread (ba0ab36f): buffering a long polyline (e.g. the ~887 km / 2,833-vtx BR-319) ran geodesicBuffer synchronously and froze the app for ~700ms on every location touch. Moved buffering/intersects/area to geometryEngineAsync (worker); shared the in-flight promise across the ~29 useLocationGeometry consumers via a cache keyed on geometry+buffer+SR.
  • Commit on release (2629974d): the slider wrote location.buffer on every pointer-move, re-firing the async buffer per tick. Drive the slider/label from local state and commit once per drag.
  • No flicker (0d5ce54c): moving buffering off-thread put an await between Sketch's eager bufferRef.removeAll() and the redraw, so the ring blinked out mid-recompute. drawBuffer now owns the buffer layer — clears the old graphic only once the new one is ready — plus a monotonic token so a stale async result can't clobber a newer one.
  • Reliable commit on fast drags (50821b97): the slider's controlled value is React state set in onValueChange; on a fast drag it hasn't flushed by pointer-up, so Radix sees no change vs slide-start and skips onValueCommit entirelylocation.buffer never updates and the ring keeps the old size (label looked right because it flushes a beat later). A track click commits via a different path, so it was always reliable. Now each change is captured in a ref and committed on a trailing debounce, independent of whether Radix fires onValueCommit.
  • Reverted an interim sync-fallback commit (0b07caa679f95708): its premise (worker rejection) never occurred in testing.

Test plan

  • Upload a long polyline (BR-319, 2,833 vtx) — app stays responsive (no main-thread freeze) while buffering.
  • Draw a point, fast-drag the buffer slider, release — ring resizes every time (not just on track clicks).
  • Drag the slider — ring swaps to the new size without blinking out.
  • Track-click the slider — still commits (regression check).
  • Confirm the SELECTED AREA value tracks the buffer value.
  • Note: the heavy 2,833-vtx line still has ~1–2s compute latency with no pending indicator (separate follow-up).

Uploading a long polyline (e.g. the ~887 km BR-319) froze the app for
seconds. The cost wasn't the input vertex count — it tracked road length,
which fingerprinted ArcGIS geodesicBuffer densifying the offset curve into
thousands of vertices. That ran synchronously on the main thread from every
path that touches a location: the upload dialog, the ~29 useLocationGeometry
consumers, and the area_afp bounds check (geometryEngine.intersects).

Move all of it to geometryEngineAsync (runs in a worker): geodesicBuffer,
intersects, and geodesicArea. getGeometryWithBuffer/useLocationGeometry become
async, and the shared buffer cache now keys on the in-flight promise so
concurrent consumers share one computation. Sketch's drawBuffer is async and
debounces the reshape redraw.

Refs: AM
Dragging the buffer slider wrote location.buffer on every pointer-move,
re-firing the async buffer for each intermediate value, so a flurry of
delayed recomputations landed after the drag ended. Drive the slider/label
from local state while dragging and commit to location on onValueCommit, so
the buffer is recomputed once per drag.

Refs: AM
Moving geodesicBuffer off-thread left the sketch buffer layer cleared
synchronously but redrawn after the worker resolved, so the buffer blinked
out on every location change (e.g. committing a buffer-slider value). A
rejected/superseded async buffer could also leave the layer empty.

Let drawBuffer own the buffer layer: clear the old graphic only once the new
one is ready, guard the redraw with a monotonic token so a stale resolve
can't clobber a newer one, and catch worker errors so a failed buffer keeps
the current one instead of clearing it.

Refs: AM
The async geodesicBuffer worker can reject or resolve empty transiently
(cold load, rapid slider commits). drawBuffer then kept the stale ring,
so the buffer on the map sometimes didn't resize after releasing the
slider. Recompute synchronously on worker failure/empty so the ring
always reflects the latest buffer; the async fast path still handles the
common case, so the long-polyline freeze stays avoided.

Refs: AM
The buffer slider is controlled by React state updated in onValueChange.
Radix only fires onValueCommit when its (controlled) value differs from
the slide-start value; on a fast drag the state hasn't flushed by
pointer-up, so Radix sees no change and skips onValueCommit entirely.
location.buffer never updates and the ring/area keep the old size, while
the label (also the local state) flushes a beat later and looks correct.
A track click commits via a different path, so it was always reliable.

Capture each change in a ref and commit the final value on a trailing
debounce off onValueChange, independent of whether Radix fires
onValueCommit. Still one commit per drag.

Refs: AM
@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
amazonia-360 Ready Ready Preview, Comment Jun 5, 2026 4:56pm

Request Review

Long polylines take ~700ms to buffer off-thread; the area and buffer
value silently lagged the slider. Expose an isCalculating flag from a
new useLocationGeometryWithStatus hook and render an inline spinner
next to the area km2 and buffer value in the create + confirm panels.
@sonarqubecloud

sonarqubecloud Bot commented Jun 5, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
36.1% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant